
package org.buildforce.tomcat;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.maven.plugin.logging.Log;

/**
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author Martin Todorov
 */
public class TomcatManager
{
    class StreamGobbler extends Thread
    {
        InputStream is;
        String type;
        
        StreamGobbler(InputStream is, String type)
        {
            this.is = is;
            this.type = type;
        }
        
        public void run()
        {
            try
            {
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                String line=null;
                while ( (line = br.readLine()) != null)
                    System.out.println(type + ">" + line);    
                } catch (IOException ioe)
                  {
                    ioe.printStackTrace();  
                  }
        }
    }

    public static final String COMMAND_UNIX_STARTUP = "bin/startup.sh";
    public static final String COMMAND_UNIX_SHUTDOWN = "bin/shutdown.sh";
    public static final String COMMAND_WINDOWS_STARTUP = "bin/startup.bat";
    public static final String COMMAND_WINDOWS_SHUTDOWN = "bin/shutdown.bat";

    private TomcatConfiguration tomcatConfiguration;
    private Log log;


    public TomcatManager(TomcatConfiguration tomcatConfiguration, Log log)
    {
        this.tomcatConfiguration = tomcatConfiguration;
        this.log = log;
    }

    public void startTomcat()
            throws TomcatExecutionException, TomcatConfigurationException
    {
        if (isTomcatRunning())
        {
            getLog().error("Tomcat is already running!");
            return;
        }

        try
        {
            // Execute the startup command
            execProcess(getStartupCommand());

            int totalSleepTime = 0;
            try
            {
                while (!isTomcatRunning())
                {
                    Thread.sleep(3000);

                    totalSleepTime += 3000;

                    if (tomcatConfiguration.getTimeout() > 0 && totalSleepTime >= tomcatConfiguration.getTimeout())
                        throw new InterruptedException("Timed out while waiting for Tomcat to startup!");
                }
            }
            catch (InterruptedException e)
            {
                throw new TomcatExecutionException("Timed out while waiting for Tomcat to startup!");
            }

            getLog().info("Tomcat started.");
        }
        catch (Exception e)
        {
            throw new TomcatExecutionException("Failed to start tomcat",e);
        }
    }

    private void execProcess(final String fileName)
            throws TomcatConfigurationException, IOException
    {
        new Thread(){

            @Override
            public void run()
            {
                try
                {
                    String[] commands = {fileName};
                    Map<String, String> envMap = System.getenv();
                    List<String> list = new ArrayList<String>();
                    for (Entry<String, String> entry : envMap.entrySet())
                    {
                        list.add(entry.getKey()+"="+entry.getValue());
                    }
                    list.add("CATALINA_HOME="+tomcatConfiguration.getTomcatHome());
                    
                    Process proc = Runtime.getRuntime().exec(commands, list.toArray(new String[]{}));
                    StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");            
                    StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");
                    
                    // kick them off
                    errorGobbler.start();
                    outputGobbler.start();
                }

                catch (Exception e)
                {
                    //throw new TomcatExecutionException("Failed to start tomcat",e);
                    e.printStackTrace();
                }
            }
        }.start();
    }

    public void stopTomcat()
            throws TomcatExecutionException, TomcatConfigurationException
    {
        if (isTomcatRunning())
        {
            try
            {
                // Execute the shutdown command
                execProcess(getShutdownCommand());

                int totalSleepTime = 0;
                try
                {
                    while (isTomcatRunning())
                    {
                        Thread.sleep(3000);

                        totalSleepTime += 3000;

                        if (tomcatConfiguration.getTimeout() > 0 && totalSleepTime >= tomcatConfiguration.getTimeout())
                            throw new InterruptedException("Timed out while waiting for Tomcat to shutdown!");
                    }
                }
                catch (InterruptedException e)
                {
                    throw new TomcatExecutionException("Timed out while waiting for Tomcat to shutdown!");
                }

                getLog().info("Tomcat stopped.");
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        else
        {
            getLog().error("Tomcat was not running!");
        }
    }

    public void restartTomcat()
            throws TomcatExecutionException, TomcatConfigurationException
    {
        getLog().info("Restarting Tomcat...");

        stopTomcat();
        startTomcat();

        getLog().info("Tomcat restarted.");
    }

    public boolean isTomcatRunning()
    {
        boolean running = false;
        Socket socket = null;
        try
        {            
            socket = new Socket(tomcatConfiguration.getHost(), tomcatConfiguration.getPort());
            running = true;
        }
        catch (IOException e)
        {
            running = false;
        }
        finally
        {
            if (socket != null)
            {
                try
                {
                    socket.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }

        return running;
    }

    public String getStartupCommand()
            throws TomcatExecutionException, TomcatConfigurationException
    {
        String binary;
        if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1)
            binary = tomcatConfiguration.getTomcatHome() +File.separator +COMMAND_UNIX_STARTUP;
        else
            binary = tomcatConfiguration.getTomcatHome() +"/" +COMMAND_WINDOWS_STARTUP;

        if (!(new File(binary)).exists())
            throw new TomcatExecutionException("Could not find Tomcat startup script ['" +binary+"']! " +
                                               "Please, check your tomcat home settings!");

        return binary;
    }

    public String getShutdownCommand()
            throws TomcatExecutionException, TomcatConfigurationException
    {
        String binary;
        if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1)
            binary = tomcatConfiguration.getTomcatHome() +File.separator +COMMAND_UNIX_SHUTDOWN;
        else
            binary = tomcatConfiguration.getTomcatHome() +File.separator +COMMAND_WINDOWS_SHUTDOWN;

        if (!(new File(binary)).exists())
            throw new TomcatExecutionException("Could not find Tomcat shutdown script ['" +binary+"']! " +
                                               "Please, check your tomcat home settings!");

        return binary;
    }

    public TomcatConfiguration getTomcatConfiguration()
    {
        return tomcatConfiguration;
    }

    public void setTomcatConfiguration(TomcatConfiguration tomcatConfiguration)
    {
        this.tomcatConfiguration = tomcatConfiguration;
    }

    public boolean isTomcatHomeSet() throws TomcatConfigurationException
    {
        return tomcatConfiguration.getTomcatHome() != null || System.getProperty("tomcat.home") != null;
    }

    public Log getLog()
    {
        return log;
    }

    public void setLog(Log log)
    {
        this.log = log;
    }
    
}
